﻿using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms.DataVisualization.Charting;

namespace 串口助手
{
    public class RealTimeCurve_Channel
    {
        [System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
        static extern int memcmp(byte[] b1, byte[] b2, long count);

        public class MsgDecode
        {
            public int data_type = 0; // 0: int, 1: float, 2: string
            public bool flag_msb = false;
            public int in_msg_pos = 1;
            public int byte_num = 4;
            public byte[] head;
        };

        public class Channel
        {
            public MsgDecode msgDecode = new MsgDecode();
            public bool enable = true;
            public float gain = 1;
            public Series series = new Series();
        };

        private double seriesY;
        private Series Series1 = new Series();
        private Thread threadSeriesWrite;
        private AutoResetEvent threadSeriesWrite_Supend = new AutoResetEvent(false);
        private volatile bool running = true;

        public Channel Create(string name, byte[] mask)
        {
            Channel channel = new Channel();
            channel.msgDecode.head = mask;
            channel.enable = true;
            channel.series.Name = name;
            channel.series.MarkerStyle = MarkerStyle.Circle;
            channel.series.ChartType = SeriesChartType.Spline;
            return channel;
        }

        private void SeriesWriteData_Thread()
        {
            while (running)
            {
                if (Series1.Points.Count > 200)
                    Series1.Points.RemoveAt(0);

                try
                {
                    Series1.Points.AddY(seriesY);
                }
                catch { }

                threadSeriesWrite_Supend.WaitOne();
            }
        }

        public void SeriesWriteData(Channel ch, double yValue)
        {
            seriesY = yValue;
            Series1 = ch.series;
            threadSeriesWrite_Supend.Set();
        }

        public bool SeriesWriteData(Channel ch, byte[] data, int length)
        {
            if (ch.msgDecode.head == null)
                return false;

            if (memcmp(data, ch.msgDecode.head, ch.msgDecode.head.Length) == 0)
            {
                if (ch.enable)
                {
                    seriesY = ch.gain * MsgDecode_GetPointY(ch.msgDecode, data, length);
                    Series1 = ch.series;
                    threadSeriesWrite_Supend.Set();
                }
                return true;
            }
            return false;
        }

        private double MsgDecode_GetPointY(MsgDecode decode, byte[] data, int length)
        {
            byte[] buf;
            double yValue = 0;

            switch (decode.data_type)
            {
                case 0: // Integer
                    if (length <= decode.in_msg_pos + decode.byte_num)
                        return seriesY;

                    buf = data.Skip(decode.in_msg_pos).Take(decode.byte_num).ToArray();
                    yValue = decode.flag_msb
                        ? buf.Aggregate(0.0, (acc, b) => (acc * 256) + b)
                        : buf.Reverse().Aggregate(0.0, (acc, b) => (acc * 256) + b);
                    break;

                case 1: // Float
                    if (length <= decode.in_msg_pos + 4)
                        return seriesY;

                    byte[] single = data.Skip(decode.in_msg_pos).Take(4).ToArray();
                    if (decode.flag_msb)
                        Array.Reverse(single);
                    yValue = BitConverter.ToSingle(single, 0);
                    break;

                case 2: // String
                    if (length <= decode.in_msg_pos)
                        return seriesY;

                    string s = Encoding.Default.GetString(data, 0, length).Substring(decode.in_msg_pos);
                    DataTypeConversion dataType = new DataTypeConversion();
                    float value = 0;

                    if (!dataType.GetStringSingle(s, ref value))
                        return seriesY;

                    yValue = s[0] == '-' ? -value : value;
                    break;
            }

            return yValue;
        }

        public void SeriesThreadCreate(Channel ch)
        {
            Series1 = ch.series;
            threadSeriesWrite_Supend.Reset();
            running = true;
            threadSeriesWrite = new Thread(SeriesWriteData_Thread) { IsBackground = true };
            threadSeriesWrite.Start();
        }

        public void SeriesThreadRelease()
        {
            running = false;
            threadSeriesWrite_Supend.Set(); // Unblock thread to exit cleanly
            threadSeriesWrite.Join(); // Ensure thread has finished before proceeding
        }

        public void SeriesClear(Channel ch)
        {
            ch.series.Points.Clear();
        }
    }
}
